using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace BardsTaleMapViewer {
  public class Spell {
    public static string [] _elementTypesString = new string [] { "Fry", "Freeze", "Shock", "Drain", "Burn", "Choke", "Steam" };

    public int _index;
    public Spellbook _spellbook;

    public Spell (int index, Spellbook spellbook) {
      _index = index;
      _spellbook = spellbook;
    }

    public string Name {
      get {
        if (_index < _spellbook._spellsName.Count)
          return (_spellbook._spellsName [_index]);
        else if ((_index >= 79) && (_index <= 89)) {
          //int b = Attribute;
          //int a = 4;
          //return (string.Format ("Damage: {0}", ));
          //return (string.Format ("Breath {0}", Dice.DiceString (a, b, 0, 1, 1)));
          return (string.Format ("Breath"));
        }
        //else if ((_index >= 90) && (_index <= 91))
        //  return ("Light");
        //else if (_index >= 92)
        //  return ("Summon");
        else
          return (null);
      }
    }

    public string Acronym {
      get {
        if (_index < _spellbook._spellsAcronym.Count)
          return (_spellbook._spellsAcronym [_index]);
        else
          return (null);
      }
    }

    public int Level {
      get {
        return (_spellbook._spellsLevel [_index]);
      }
    }

    public string LevelCode {
      get {
        if (_index < _spellbook._spellsLevel.Count)
          return (Level.ToString ());
        else
          return ("*");
      }
    }

    public string LevelString {
      get {
        if (_index < _spellbook._spellsLevel.Count)
          return (Level.ToString ());
        else
          return (null);
      }
    }

    public int SpellPointCost {
      get {
        return (_spellbook._spellsSpellPointCost [_index]);
      }
    }

    public string SpellPointCostString {
      get {
        if (_index < _spellbook._spellsSpellPointCost.Count)
          return (SpellPointCost.ToString ());
        else
          return (null);
      }
    }

    private int Bits {
      get {
        return (_spellbook._spellsBits [_index]);
      }
    }

    public int TargetType {
      get {
        return ((Bits >> 0) & 0x07);
      }
    }

    public string TargetTypeString {
      get {
        if (TargetType == 0)
          return ("1 member0");
        else if (TargetType == 1)
          return ("1 member1");
        else if (TargetType == 2)
          return ("1 group");
        else if (TargetType == 3)
          return ("1 foe");
        else if (TargetType == 4)
          return ("- (4)");
        else if (TargetType == 5)
          return ("- (5)");
        else if (TargetType == 6)
          return ("ALL foes");
        else
          throw new Exception ();
        //return (TargetType.ToString ());
      }
    }

    public int BitsAllowedInCombat {
      get {
        return ((Bits >> 3) & 0x01);
      }
    }

    public int BitsAllowedOutCombat {
      get {
        return ((Bits >> 4) & 0x01);
      }
    }

    public int BitsParam {
      get {
        return ((Bits >> 5) & 0x07);
      }
    }

    public string BitsParamString {
      get {
        // Spell_ApplyDamageAndAttackEffect
        if ((FunctionEnum == Spellbook.FunctionsEnum.DamageSingleTarget) ||
            //(FunctionEnum == Spellbook.FunctionsEnum.StatusPoisonOldEtcUnk) || // use CombatEffect first
            (FunctionEnum == Spellbook.FunctionsEnum.Drain)) {
          return ("Element: " + _elementTypesString [BitsParam]);
        }
        // Spell_DamageGroup_Helper
        else if ((FunctionEnum == Spellbook.FunctionsEnum.DamageGroup) ||
                 (FunctionEnum == Spellbook.FunctionsEnum.DamageAllGroups)) {
          return ("Element: " + _elementTypesString [BitsParam]);
        }
        // Spell_RepelUndead (=> Spell_DamageGroup)
        else if (FunctionEnum == Spellbook.FunctionsEnum.RepelUndead) {
          return ("Element: " + _elementTypesString [BitsParam]);
        }
        else if (FunctionEnum == Spellbook.FunctionsEnum.Heal) {
          if (BitsParam == 6)
            return ("Cure: Poss");
          else if (BitsParam == 7)
            return ("Resurrect");
          else if (BitsParam == 4)
            return ("Cure: PoisParaNuts");
          else if (BitsParam == 0)
            return ("-");
          throw new Exception ();
        }
        else if (FunctionEnum == Spellbook.FunctionsEnum.Eye) {
          if ((BitsParam & 0x01) != 0)
            throw new Exception ();
          int detectionPower = BitsParam >> 1;
          bool detectStairs = false;
          bool detectTrap = false;
          bool detectSpecial = false;
          if (detectionPower != 0)
            detectStairs = true;
          if (detectionPower != 1)
            detectTrap = true;
          if (detectionPower == 2)
            detectSpecial = true;
          return (string.Format ("Detect: {0}{1}{2}", detectStairs ? "S" : "-", detectTrap ? "T" : "-", detectSpecial ? "X" : "-"));
        }
        else if (BitsParam == 0)
          return ("-");
        return (string.Format ("?: {0}", BitsParam));
        //throw new Exception ();
      }
    }

    private int Function {
      get {
        if (_index < _spellbook._spellsFunction.Count)
          return (_spellbook._spellsFunction [_index]);
        else if ((_index >= 79) && (_index <= 89))
          return ((int) Spellbook.FunctionsEnum.DamageGroup);
        else if ((_index == 90) || (_index == 91))
          return ((int) Spellbook.FunctionsEnum.Light);
        else if (_index >= 92)
          return ((int) Spellbook.FunctionsEnum.Summon);
        return ((int) Spellbook.FunctionsEnum.None);
      }
    }

    public Spellbook.FunctionsEnum FunctionEnum {
      get {
        return ((Spellbook.FunctionsEnum) Function);
      }
    }

    public string FunctionName {
      get {
        //return (_spellbook._functionsName [Function]);
        return (FunctionEnum.ToString ());
      }
    }

    public string ClassCode {
      get {
        if (_index <= 21)
          return ("C");
        else if (_index <= 43)
          return ("S");
        else if (_index <= 65)
          return ("M");
        else if (_index <= 78)
          return ("W");
        else
          return ("*");
      }
    }

    public string ClassString {
      get {
        if (_index <= 21)
          return ("Conjurer");
        else if (_index <= 43)
          return ("Sorcerer");
        else if (_index <= 65)
          return ("Magician");
        else if (_index <= 78)
          return ("Wizard");
        else
          return (null);
      }
    }

    private int Attribute {
      get {
        return (_spellbook._spellsAttribute [_index]);
      }
    }

    public string AttributeString (bool html) {
      if (FunctionEnum == Spellbook.FunctionsEnum.Heal) {
        int healPower = (Attribute >> 0) & 0x0F;
        int healTarget = (Attribute >> 7) & 0x1;
        string healTargetString = (healTarget == 0) ? "One" : "All";
        int unkheal = (Attribute >> 4) & 0x7;
        if (unkheal != 0)
          throw new Exception ();
        if (healPower == 0)
          return (string.Format ("Target: {0}", healTargetString));
        else if (healPower == 15)
          return (string.Format ("Target: {0} HP: Full", healTargetString));
        else
          return (string.Format ("Target: {0} HP: {1}", healTargetString, Dice.DiceWithMinMaxString (4, healPower, 0, 1, 1, 1, 1)));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.Light) {
        int lightType = Attribute;
        int lightRange = _spellbook._lightTypesDungeonDrawingDepth [lightType];
        int lightBaseDuration = _spellbook._lightTypesMaxDuration [lightType];
        string lightDurationString = GetDurationString (lightBaseDuration);
        int lightSeeHidden = _spellbook._lightTypesSeeHidden [lightType];
        return (string.Format ("Range: {0} Duration: {1} SeeHidden: {2}", lightRange, lightDurationString, lightSeeHidden != 0));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.Summon) {
        int summonType = (Attribute >> 0) & 0x3F;
        int variation = (Attribute >> 6) & 0x1;
        int isIllusion = (Attribute >> 7) & 0x1;
        int monsterTypePlus1 = _spellbook._summonTypesMonsterTypePlus1 [summonType];
        if (monsterTypePlus1 < 5) {
          if (variation != 0)
            throw new Exception ();
          int specialIndex = monsterTypePlus1 >> 1;
          return (string.Format ("Special: {0}{2}IsIllusion: {1}", _spellbook._game._beastiary._specials [specialIndex], isIllusion != 0, html ? "<br>" : " "));
        }
        else if (variation == 0) {
          MonsterType monsterType = _spellbook._game._beastiary._monsterTypes [monsterTypePlus1 - 1];
          return (string.Format ("Type: {0}{2}IsIllusion: {1}", monsterType.NameSingular, isIllusion != 0, html ? "<br>" : " "));
        }
        else { // variation == 1
          int monsterType2Plus1 = _spellbook._summonTypesMonsterTypePlus1 [summonType + 1];
          MonsterType monsterType1 = _spellbook._game._beastiary._monsterTypes [monsterTypePlus1 - 1];
          MonsterType monsterType2 = _spellbook._game._beastiary._monsterTypes [monsterType2Plus1 - 1];
          return (string.Format ("Type: {0}/{1}{3}IsIllusion: {2}", monsterType1.NameSingular, monsterType2.NameSingular, isIllusion != 0, html ? "<br>" : " "));
        }
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.GroupMalusAC) {
        int freezeType = Attribute;
        int armorClassMalus = _spellbook._groupArmorClassMalusTypesValue [freezeType];
        return (string.Format ("{0}", armorClassMalus));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.SelfBonusAC) {
        int selfArmorClassBonusType = Attribute;
        int armorClassBonus = _spellbook._selfArmorClassBonusTypesValue [selfArmorClassBonusType];
        return (string.Format ("{0}", armorClassBonus));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.GroupBonusAC) {
        int groupArmorClassBonusType = Attribute;
        int armorClassBonus = _spellbook._groupArmorClassBonusTypesValue [groupArmorClassBonusType];
        return (string.Format ("{0}", armorClassBonus));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.BonusDamage) {
        int damageBonusType = Attribute;
        int damageBonus = _spellbook._damageBonusTypesValue [damageBonusType];
        return (string.Format ("{0}", damageBonus));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.MalusToHit) {
        int toHitMalusType = Attribute;
        int toHitMalus = _spellbook._damageMalusTypesValue [toHitMalusType];
        return (string.Format ("{0}", toHitMalus));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.PartyBonusAC) {
        int baseDuration = Attribute;
        string durationString = GetDurationString (baseDuration);
        return (string.Format ("2 Duration: {0}", durationString));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.Levitation) {
        int levitationType = Attribute;
        int baseDuration = _spellbook._levitationTypesDuration [levitationType];
        string durationString = GetDurationString (baseDuration);
        return (string.Format ("Duration: {0}", durationString));
      }
      else if ((FunctionEnum == Spellbook.FunctionsEnum.Compass) ||
               //(FunctionEnum == Spellbook.FunctionsEnum.Compass) ||
               (FunctionEnum == Spellbook.FunctionsEnum.Eye)) {
        int baseDuration = Attribute;
        string durationString = GetDurationString (baseDuration);
        return (string.Format ("Duration: {0}", durationString));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.Drain) {
        int b = Attribute + 1;
        int a = 4;
        return (string.Format ("Damage: {0}", Dice.DiceWithMinMaxString (a, b, 0, 1, 1, 1, 1)));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.Status) {
        int attackEffect = Attribute;
        string attackEffectString = MonsterType._attackEffectsString [attackEffect];
        return (string.Format ("{0}", attackEffectString));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.MemberBonusDamageDices) {
        int b = Attribute;
        int a = 8;
        return (string.Format ("Damage: {0}", Dice.DiceWithMinMaxString (a, b, 0, 1, 1, 1, 1)));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.DamageSingleTarget) {
        int nb = Attribute;
        int a = 4;
        //int b = level + 1;
        return (string.Format ("Damage: {0}x{1}d{2,1}", nb, "(level+1)", a));
      }
      else if ((FunctionEnum == Spellbook.FunctionsEnum.DamageGroup) ||
               (FunctionEnum == Spellbook.FunctionsEnum.DamageAllGroups)) {
        int b = Attribute;
        int a = 4;
        return (string.Format ("Damage: {0}", Dice.DiceWithMinMaxString (a, b, 0, 1, 1, 1, 1)));
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.DispelIllusion) {
        int b = Attribute;
        // detect doppleganger, kill illusions and any new illusions
        if (b == 0xFF)
          return (string.Format ("DetectDoppleganger: {0} Duration: Infinite", true));
        else if (b == 0x01)
          return (string.Format ("DetectDoppleganger: {0} Duration: 1 round", false));
        else
          throw new Exception ();
      }
      else if (FunctionEnum == Spellbook.FunctionsEnum.RepelUndead) {
        int listOffset;
        string types;
        if (_index <= 68) {
          types = "Undeads";
          listOffset = 0;
        }
        else {
          types = "Demons";
          listOffset = 11;
        }
        List<string> names = new List<string> ();
        for (int i = 0; i < 11; i++) {
          int monsterTypePlus1 = _spellbook._repelUndeadMonsterTypesPlus1 [listOffset + i];
          if (monsterTypePlus1 != 0xFF)
            names.Add (_spellbook._game._beastiary._monsterTypes [monsterTypePlus1 - 1].NameSingular);
        }
        string namesString = string.Join (",", names);
        //string part1 = string.Format ("Repel: {0}: {1}", types, namesString);
        // DamageGroup
        if ((_index == 67) || (_index == 75)) {
          int b = Attribute;
          int a = 4;
          return (string.Format ("Damage: {0}{3}{1} ({2})", Dice.DiceWithMinMaxString (a, b, 0, 1, 1, 1, 1), types, namesString, html ? "<br>" : " "));
        }
        // Drain
        else {
          int b = Attribute + 1;
          int a = 4;
          return (string.Format ("Damage: {0}{3}{1} ({2})", Dice.DiceWithMinMaxString (a, b, 0, 1, 1, 1, 1), types, namesString, html ? "<br>" : " "));

        }
      }
      else if ((FunctionEnum == Spellbook.FunctionsEnum.TrapZap) ||
           (FunctionEnum == Spellbook.FunctionsEnum.Teleport) ||
           (FunctionEnum == Spellbook.FunctionsEnum.Possess) ||
           (FunctionEnum == Spellbook.FunctionsEnum.PhaseDoor) ||
           (FunctionEnum == Spellbook.FunctionsEnum.AntiMagic) ||
           (FunctionEnum == Spellbook.FunctionsEnum.Locate) ||
           (FunctionEnum == Spellbook.FunctionsEnum.Blind)) {
        if (Attribute != 0)
          throw new Exception ();
        return ("-");
      }
      return (string.Format ("?: {0}", Attribute.ToString ()));
    }

    public string Parms {
      get {
        string parms = "";
        if (BitsParamString != "-")
          parms += " " + BitsParamString;
        if (AttributeString (false) != "-")
          parms += " " + AttributeString (false);

        parms = parms.Trim ();
        if (parms == "")
          parms = "-";
        return (parms);
        //string.Format ("{0}{1}", () ? "" : BitsParamString, (AttributeString == "-") ? "" : AttributeString);

        //if (desc == "")
        //  return (string.Format ("{0}", Name));
        //else
        //  return (string.Format ("{0}: {1}", Name, desc));
      }
    }

    public string ParmsHtml {
      get {
        string parms;
        string bitsParamString = BitsParamString;
        string attributeString = AttributeString (true);
        if ((bitsParamString != "-") && (attributeString != "-"))
          parms = $"{bitsParamString}<br>{attributeString}";
        else if (bitsParamString != "-")
          parms = bitsParamString;
        else if (attributeString != "-")
          parms = attributeString;
        else
          parms = "&nbsp;";
        return (parms);
        //string.Format ("{0}{1}", () ? "" : BitsParamString, (AttributeString == "-") ? "" : AttributeString);

        //if (desc == "")
        //  return (string.Format ("{0}", Name));
        //else
        //  return (string.Format ("{0}: {1}", Name, desc));
      }
    }

    public string ShortDesc {
      get {
        string desc = "";

        if (Name != null)
          desc += Name + ":";
        desc += " " + FunctionName;

        string parms = Parms;
        if (parms != "-")
          desc += ": " + parms;
        return (desc.Trim ());
        //string.Format ("{0}{1}", () ? "" : BitsParamString, (AttributeString == "-") ? "" : AttributeString);

        //if (desc == "")
        //  return (string.Format ("{0}", Name));
        //else
        //  return (string.Format ("{0}: {1}", Name, desc));
      }
    }

    public string ShortDescHtml {
      get {
        string desc = "";

        if (Name != null)
          desc += Name + "<br>";
        desc += FunctionName;

        string parms = ParmsHtml;
        if (parms != "&nbsp;")
          desc += "<br>" + parms;
        return (desc);
        //string.Format ("{0}{1}", () ? "" : BitsParamString, (AttributeString == "-") ? "" : AttributeString);

        //if (desc == "")
        //  return (string.Format ("{0}", Name));
        //else
        //  return (string.Format ("{0}: {1}", Name, desc));
      }
    }

    private static string GetDurationString (int baseDuration) {
      string durationString;
      if (baseDuration == 0xFF)
        durationString = "Infinite";
      else
        durationString = string.Format ("[{0};{1}]", baseDuration + 1 + 0, baseDuration + 1 + 15);
      return (durationString);
      //sb.AppendFormat ("  AAAAAAAAAA", BBBBBBBBBBBBB);
    }

    public void Dump () {
      StringBuilder sb = new StringBuilder ();
      sb.AppendFormat ("{0,3}", _index);
      sb.AppendFormat ("  {0,1}", ClassCode);
      sb.AppendFormat ("  {0,1}", LevelCode);
      sb.AppendFormat ("  {0,-4}", Acronym ?? "*");
      sb.AppendFormat ("  {0,-15}", Name ?? "*");
      sb.AppendFormat ("  {0,-22}", FunctionName);
      sb.AppendFormat ("  {0,-2}", SpellPointCostString ?? "*");
      sb.AppendFormat ("  {0,-9}", TargetTypeString);
      sb.AppendFormat ("  {0,3}", BitsAllowedInCombat);
      sb.AppendFormat ("  {0,3}", BitsAllowedOutCombat);
      sb.AppendFormat ("  {0}", Parms);
      Console.WriteLine (sb.ToString ());
    }

    public StringBuilder DumpHtml () {
      StringBuilder sb = new StringBuilder ();
      sb.AppendFormat ("<tr>");
      sb.AppendFormat ("<th>{0}</th>", _index);
      sb.AppendFormat ("<td>{0}</td>", ClassString ?? "&nbsp;");
      sb.AppendFormat ("<th>{0}</th>", LevelString ?? "&nbsp;");
      sb.AppendFormat ("<th style='font-family: monospace;font-size: 13px;'>{0}</th>", Acronym ?? "&nbsp;");
      sb.AppendFormat ("<td style='white-space:nowrap'>{0}</td>", Name ?? "&nbsp;");
      sb.AppendFormat ("<td>{0}</td>", FunctionName);
      sb.AppendFormat ("<th>{0}</th>", SpellPointCostString ?? "&nbsp;");
      sb.AppendFormat ("<th style='white-space:nowrap'>{0}</th>", TargetTypeString);
      sb.AppendFormat ("<th>{0}</th>", BitsAllowedInCombat == 1 ? "True" : "False");
      sb.AppendFormat ("<th>{0}</th>", BitsAllowedOutCombat == 1 ? "True" : "False");
      //sb.AppendFormat ("<td style='text-align: inherit'>{0}</td>", ParmsHtml);
      sb.AppendFormat ("<td style='white-space:nowrap'>{0}</td>", ParmsHtml);
      sb.AppendFormat ("</tr>");
      return (sb);
    }
  }
}
